import java.util.ArrayList;
import java.util.Hashtable;
import java.util.Scanner;
import java.util.TreeSet;
import java.io.File;
import java.io.FileNotFoundException;
import java.util.*;

/**
 * 
 * Stores objects of FoodOrder class
 * 
 */
public class Orders extends Observable implements Runnable 
{
	int dishcount = 0;
	File ordersFile ;
	Scanner scanner ;
	String inputRow = null;
	int valueNew = 0;

	ArrayList<FoodOrder> fo = new ArrayList<FoodOrder>();
	ArrayList<FoodOrder> hatch = new ArrayList<FoodOrder>();
	KitchenToHatch kth = new KitchenToHatch();
	MenuList menu;
	private Set<FoodOrder> Orders;
	Boolean finished = false;
	private FoodOrder oneOrder;
	TreeSet<MenuItem> dish;
	public Orders() 
	{
		ordersFile = new File("FoodOrder.csv");
		try {
			scanner = new Scanner(ordersFile);
		} catch (FileNotFoundException e) {
			
			e.printStackTrace();
		}
	}

	/**
	 * @return the orders
	 */
	public final Set<FoodOrder> getOrders() {
		return Orders;
	}

	/**
	 * @param orders
	 *            the orders to set
	 */
	public final void setOrders(Set<FoodOrder> orders) {
		Orders = orders;
	}

	/**
	 * Populate the array list from a text file
	 * 
	 * @param File
	 *            to be read
	 */

	public synchronized void run()

	{
		ArrayList<String> allDishes = new ArrayList<String>();
		String strQty = "";
		
		while (scanner.hasNextLine()) 
		{
			try 
			{
				// read first line and process it
				String inputLine = scanner.nextLine();
				if (inputLine.length() != 0) 
				{											// ignored if blank line
					dishcount++;
					String parts[] = inputLine.split(",");
					String tableName = parts[0].trim(); 	// for table name
					String item = parts[1].trim(); 			// menu item only
					strQty = parts[2].trim();				// for quantity
					int qty = Integer.parseInt(strQty);
					oneOrder = new FoodOrder(tableName,item,qty);
					fo.add(oneOrder);
					if(dishcount %3 ==0)
					{
						FoodOrder temp =new FoodOrder(fo.get(0));
						fo.remove(0);
						hatch.add(temp);	// deletes the first element of the Array List in every iteration 	
						kth.hatchUpdate(hatch);
					}
					Thread.sleep(1000);
					setChanged();
					notifyObservers();
			    	clearChanged();
				}
				/*if(!scanner.hasNext())
				{
					while(!fo.isEmpty())
					{
						dishcount++;
						if(dishcount %3 ==0)
						{
					//		hatch.add(fo.remove(0));	// deletes the first element of the Array List in every iteration 	
						//	kth.hatchUpdate(hatch);
						}
						Thread.sleep(500);
						setChanged();
						notifyObservers();
				    	clearChanged();
					}
				}*/
			}
	
			// ignore lines in error and try and carry on
	
			// this catches trying to convert a String to an integer
			catch (NumberFormatException nfe) {
				String error = "Number conversion error in  - " + nfe.getMessage();
				System.out.println(error);
			}
			// this catches missing items if only one or two items
			// other omissions will result in other errors
			catch (Exception e) {
				String error = "Error1 : '" + "' index position : "
						+ e.getMessage();
				System.out.println(error);
			} 
		}
		/*finished = true;
		if(finished)
		{
			try
			{
				while(!fo.isEmpty())
				{
					dishcount++;
					//System.out.println(getKitchenOrder());
					if(dishcount %3 ==0)
					{
						hatch.add(fo.remove(0));	// deletes the first element of the Array List in every iteration 	
						kth.hatchUpdate(hatch);
					}
					Thread.sleep(500);
					setChanged();
					notifyObservers();
			    	clearChanged();
				}
			}
			catch (NumberFormatException nfe) {
				String error = "Number conversion error in  - " + nfe.getMessage();
				System.out.println(error);
			}
			// this catches missing items if only one or two items
			// other omissions will result in other errors
			catch (Exception e) {
				String error = "Error  : '" + "' index position : "
						+ e.getMessage();
				System.out.println(error);
			}
		}*/
	}


	// *********************************************************************************************************************************************
	//  															FUNCTIONS


	/** Function for finding frequency of dishes ordered **/
	public String getOrdersFreq()
	{
		Hashtable<String, Integer> dishFreq = new Hashtable<String, Integer>(); // Hashtable for storing dish name and its frequency
		ArrayList<String> uniqueOrders = new ArrayList<String>();               // Array for keeping list of unique dishes
		String dishname, freqList = String.format("%-34s","\n    Dish Name") + "Frequency\n\n";;

		try 
		{
			for(FoodOrder fo : Orders)
			{
				if(uniqueOrders.contains(dishname = fo.getItem()))      // Checks if item exists is unique list
				{
					int count = dishFreq.get(dishname) + fo.getQuantity();  // If dish exists, previous quantity added with new quantity
					dishFreq.put(dishname, count);                                                  // Adds dish with new frequency to hashtable
				}
				else
				{
					uniqueOrders.add(dishname);                                                     // If item doesn't exist, dishname added to unique list
					dishFreq.put(dishname, fo.getQuantity());                       // and also to hashtable with the quantity ad the value 
				}                                                                                                               // and dishname as the key              
			}

			for(int j=0; j<uniqueOrders.size(); j++)
			{
				/** Adds to string, the name of dish and the total frequency of dish **/
				freqList += String.format("%-38s",uniqueOrders.get(j))+ dishFreq.get(uniqueOrders.get(j)) + "\n";
			}
			freqList += "\n";
		} 

		
		catch (Exception e) 
		{       
			e.printStackTrace();
		}
		return freqList;
	}

	/** Function to get a list of all dishes that remained unordered **/
	public String getUnorderedDishList()
	{
		ArrayList<String> allDishes = new ArrayList<String>();
		ArrayList<String> orderedDishes = new ArrayList<String>();                              // Array list to keep all names of dishes
		String UDList = "Unordered Dishes\n\n";
		try 
		{
			for(MenuItem allDish : dish)
				allDishes.add(allDish.getDishName());           // Store all the dishes of menu in separate list
			for(FoodOrder Fo : Orders)
				orderedDishes.add(Fo.getItem());                        // Adds each dish item to array list

			ArrayList<String> unorderedDish = new ArrayList<String>(allDishes);
			unorderedDish.removeAll(orderedDishes);
			for(int j=0; j<unorderedDish.size(); j++)
			{
				UDList += "   " + unorderedDish.get(j) + "\n";
			}
		} 

		catch (Exception e) 
		{
			e.printStackTrace();
		}
		return UDList;
	}

	/** Function for printing the orders for the evening 
	 * This function is not required but created during testing
	 * to check for errors and exceptions
	 * **/
	public void printOrders()
	{
		String otput = String.format("%-10s","Table Name") + "\t" + String.format("%-23s","   Dish Name") 
				+ "\t" + String.format("%-10s","     Quantity") + "\n\n";
		try 
		{
			for(FoodOrder fo : Orders)
			{
				otput +=  "  " +String.format("%-10s",fo.getTableName()) + "\t" + String.format("%-30s",fo.getItem()) 
						+ "\t" + String.format("%-10s",fo.getQuantity()) + "\n";
			}
			System.out.println(otput);
		} 

		catch (Exception e) 
		{       
			e.printStackTrace();
		}
	}

	/** Prints the bill for the table **/
	public String getTableBill(String TableName)
	{
		String bill = TableName + "\n\n";
		double cost = 0.0;
		double discountedBill;
		try
		{
			for(FoodOrder fo : Orders)                      // Iterates through the food orders
			{
				if(fo.getTableName().equals(TableName))         // Checks where the table name matches
				{
					for(MenuItem mi : dish)                                 // then iterates through the menu
					{
						if(fo.getItem().equals(mi.getDishName()))       // and matches the dish of food order with menu
						{
							bill += String.format("%-30s",fo.getItem()) + "\t  " + fo.getQuantity()         // and creates the bill 
									+ " * " + String.format("%-5s",mi.getPrice()) +" = "                            
									+ String.format("%5s",fo.getQuantity() * mi.getPrice()) + "\n";
							cost += fo.getQuantity() * mi.getPrice();
						}
					}
				}
			}
		}

		catch (Exception e) 
		{
			e.printStackTrace();
		}

		bill += String.format("%-28s","") + "\t" + "--------------------";                                              // Prints bill in a neat format
		bill += String.format("%-25s","\nTotal") + "\t\t      " + String.format("%5s",cost);
		bill += String.format("%-30s","\n\nTotal Bill") + "\t : " + String.format("%2s",cost);
		discountedBill = getDiscountedCost(TableName,cost);                                                                             // Gets the discounted bill after passing table name and
		double discount = cost-discountedBill;                                                                                                  // actual bill 
		bill += String.format("%-30s","\nDiscount Offered") + "\t : " + String.format("%.2f",discount);
		bill += String.format("%-30s","\nDiscounted Bill") + "\t : " + discountedBill;
		return bill;
	}

	/** Prints just the total cost of the table for the evening **/
	public String getTableCost()
	{
		ArrayList<String> uniqueTableList = new ArrayList<String>();    // Keeps record of all unique table names
		String tableName, tName = null, max = null;
		double bill;
		Hashtable<String, String> costs = new Hashtable<String, String>();
		String output = "Table Summary for this Evening\n\n";

		/** The following function iterates through
		 *  food order file and stores all unique table names
		 */
		try 
		{
			for(FoodOrder fo : Orders)                                                                              // Iterates through food orders
			{
				if(!(uniqueTableList.contains(tableName = fo.getTableName())))  // Checks if table name already exists in unique list
				{                                                                                                                               // if not, it adds the table
					uniqueTableList.add(tableName);                                                         
				}
			}
			for(String s : uniqueTableList)                                                         // For every unique table name
			{
				bill = 0.0;
				for(FoodOrder f : Orders)                                                               // food order is iterated
				{
					if(f.getTableName().equals(s))                                          // matched with table name
					{
						for(MenuItem mi : dish)                                                 // iterated through menu
						{
							if(f.getItem().equals(mi.getDishName()))        // matches dish from menu
							{
								bill += f.getQuantity() * mi.getPrice();        // makes bill by multiplying quantity and price
							}
						}
					}
				}
				output += "   Table Name : " + s + "\n   Total Cost : " + getDiscountedCost(s,bill) + "\n\n";
				costs.put(s,String.valueOf(getDiscountedCost(s,bill)));                 // all bills (discounted) are stored with table names
			}     																		// in a hastable, where table name is key and bill is value
			max = Collections.max(costs.values());       	// Maximum bill is calculated
			for(Object o : costs.entrySet())
			{
				Map.Entry entry = (Map.Entry) o;                                                        
				if(entry.getValue().equals(max))            	// Table name o maximum bill is found
					tName = (String) entry.getKey(); 
			}
		} 

		catch (Exception e) 
		{
			e.printStackTrace();
		}
		output += "\nTable " + tName + " has the largest bill of the evening - " + max; // Stat is added to output
		return output;
	}

	/** Function that calculates discounted price for each table
	 * depending on which table is in use
	 */
	public double getDiscountedCost(String tableName, double bill)
	{
		String tName = tableName.toLowerCase();
		double discount = 0.0, total;
		if(tName.contains("red"))
			discount = 0.05;
		else
			if(tName.contains("green"))
				discount = 0.07;
			else
				if(tName.contains("blue"))
					discount = 0.10;
				else
					System.out.println("Sorry, wrong table name");
		total = bill - (discount * bill);
		return total;

	}       

	public String getCuisineFreq()
	{
		Hashtable<String, Integer> cuisineFreq = new Hashtable<String, Integer>();      // Hashtable for storing cuisine name and its frequency
		ArrayList<String> uniqueCuisine = new ArrayList<String>();              // Array for keeping list of unique cuisines
		String cuisineName, Cuisine = "";
		String freqList = String.format("%-34s","\n    Dish Name") + "Frequency\n\n";

		try 
		{
			for(FoodOrder fo : Orders)
			{
				for(MenuItem mi : dish)
				{
					if(fo.getItem().equals(mi.getDishName()))       
					{
						if(uniqueCuisine.contains(cuisineName = mi.getCuisineName()))   // Checks if item exists is unique list
						{
							int count = cuisineFreq.get(cuisineName) + 1;   // If cuisine exists, previous count plus one
							cuisineFreq.put(cuisineName, count);                                                    // Adds cuisine with new count to hashtable
						}
						else
						{
							uniqueCuisine.add(cuisineName);                                                 // If item doesn't exist, cuisine name added to unique list
							cuisineFreq.put(cuisineName, 1);                                                // and also to hashtable with the quantity as the value 
						}                                                                                                                       // and cuisine name as the key          
					}
				}
			}

			for(int j=0; j<uniqueCuisine.size(); j++)
			{
				/** Adds to string, the name of dish and the total frequency of dish **/
				freqList += String.format("%-38s",uniqueCuisine.get(j))+ cuisineFreq.get(uniqueCuisine.get(j)) + "\n";
			}
			int max = Collections.max(cuisineFreq.values());
			for(Object o : cuisineFreq.entrySet())
			{
				Map.Entry entry = (Map.Entry) o;
				if(entry.getValue().equals(max))
					Cuisine = (String) entry.getKey(); 
			}
			freqList += "\n" + Cuisine + " cuisine is much preferred this evening!";                
		} 

		catch (Exception e) 
		{       
			e.printStackTrace();
		}
		return freqList;
	}
	
	public String getKitchenOrder()
	{
		String rep = "";
		for(FoodOrder food : fo)
		{
			rep += String.format("%-10s",food.getTableName()) + "\t" + String.format("%-30s",food.getItem()) 
			+ "\t" + String.format("%-10s",food.getQuantity()) + "\n";
		}
		return rep;
	}
	
	
	public String getTableOrder()
	{
		String rep = "";
		for(FoodOrder food : fo)
		{
			rep += String.format("%-30s",food.getItem()) + "\t" + String.format("%-10s",food.getQuantity()) + "\n";
		}
		return rep;
	}

}
